#include "include/DataTime.h"
#include <sstream>
#include <iomanip>

namespace Tool
{
DataTime::DataTime()
    : m_timePoint(std::chrono::system_clock::now())
{
}

DataTime::DataTime(time_point tp)
    : m_timePoint(tp)
{
}

void DataTime::SetTime(const time_point &tp)
{
    m_timePoint = tp;
}

DataTime::time_point DataTime::GetTime() const
{
    return m_timePoint;
}

DataTime DataTime::CurrentDateTime()
{
    return DataTime(std::chrono::system_clock::now());
}

DataTime DataTime::Now()
{
    return DataTime(std::chrono::system_clock::now());
}

int64_t DataTime::ToSecsSinceEpoch() const
{
    return std::chrono::duration_cast<std::chrono::seconds>(m_timePoint.time_since_epoch()).count();
}

int64_t DataTime::ToMSecsSinceEpoch() const
{
    return std::chrono::duration_cast<std::chrono::milliseconds>(m_timePoint.time_since_epoch()).count();
}

DataTime DataTime::FromSecsSinceEpoch(int64_t secs)
{
    return DataTime(time_point(std::chrono::seconds(secs)));
}

DataTime DataTime::FromMsecsSinceEpoch(int64_t msecs)
{
    return DataTime(time_point(std::chrono::milliseconds(msecs)));
}

int64_t DataTime::CurrentSeconds()
{
    return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}

int64_t DataTime::CurrentMilliseconds()
{
    return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}

std::string DataTime::ToString(const std::string& fmt) const
{
    auto now_c = std::chrono::system_clock::to_time_t(m_timePoint);
    std::tm buf {};
#if defined(_WIN32) || defined(_WIN64)
    if (localtime_s(&buf, &now_c) != 0)
#else
    if (localtime_r(&now_c, &buf) == nullptr)
#endif
    {
        throw std::runtime_error("Failed to convert time to local time.");
    }

    auto duration_since_epoch = m_timePoint.time_since_epoch();
    auto millisecs_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(duration_since_epoch).count();
    long milliseconds = millisecs_since_epoch % 1000;
    std::ostringstream ss;
    for (size_t i = 0; i < fmt.length(); ++i)
    {
        if (fmt[i] == '%' && i + 1 < fmt.length())
        {
            if (fmt.substr(i, 3) == "%ms")
            {
                ss << std::setfill('0') << std::setw(3) << milliseconds;
                i += 2;
            }
            else
            {
                ss << std::put_time(&buf, (fmt.substr(i, 2)).c_str());
                ++i;
            }
        }
        else
        {
            ss << fmt[i];
        }
    }
    return ss.str();
}

bool operator==(const DataTime &lhs, const DataTime &rhs)
{
    return lhs.GetTime() == rhs.GetTime();
}

bool operator!=(const DataTime &lhs, const DataTime &rhs)
{
    return !(lhs == rhs);
}

bool operator<(const DataTime &lhs, const DataTime &rhs)
{
    return lhs.GetTime() < rhs.GetTime();
}

bool operator<=(const DataTime &lhs, const DataTime &rhs)
{
    return !(rhs < lhs);
}

bool operator>(const DataTime &lhs, const DataTime &rhs)
{
    return rhs < lhs;
}

bool operator>=(const DataTime &lhs, const DataTime &rhs)
{
    return !(lhs < rhs);
}
}
